using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using UMC.Bridge;
using UMC.Data;
using UMC.ITME.Entities;
using UMC.Net;

namespace UMC.ITME
{
    public abstract class NetBridgeReader : INetBridgeReader
    {
        Channel<Tuple<long, int>> channel;
        public virtual void Receive(byte[] buffer, int offset, int size)
        {
            if (this.channel == null)
            {
                this.channel = Channel.CreateBounded<Tuple<long, int>>(new BoundedChannelOptions(10000)
                {
                    SingleReader = true,
                    SingleWriter = true,
                    FullMode = BoundedChannelFullMode.Wait,
                    AllowSynchronousContinuations = false
                });
                Receive(channel.Reader);
            }
            if (size == 0)
            {
                this.channel.Writer.TryWrite(Tuple.Create(0L, 0));
            }
            else
            {
                this.channel.Writer.TryWrite(Tuple.Create(NetCache.Share.Append(buffer, offset, size), size));
            }
        }
        public abstract bool IsOver { get; }
        async void Receive(ChannelReader<Tuple<long, int>> reader)
        {
            var buffer = ArrayPool<byte>.Shared.Rent(600);
            try
            {
                while (await reader.WaitToReadAsync())
                {
                    if (reader.TryRead(out var result))
                    {
                        if (result.Item2 > 0)
                        {
                            using (var view = NetCache.Share.CreateAccessor(result.Item1, result.Item2))
                            {

                                int size = 0;
                                while ((size = await view.ReadAsync(buffer, 0, buffer.Length)) > 0)
                                {
                                    await this.WriteAsync(buffer, 0, size);
                                }
                            }
                        }
                        else
                        {
                            break;
                        }

                    }
                }
                this.Dispose();
            }
            catch
            {

                this.Dispose();
            }
            finally
            {

                ArrayPool<byte>.Shared.Return(buffer);
            }
        }
        public virtual void Dispose()
        {
            this.channel?.Writer?.TryComplete();

        }
        protected abstract Task WriteAsync(byte[] buffer, int offset, int count);
    }
    public class Socks5Server : NetBridgeReader, IDisposable
    {
        public static byte[] Check(StringValue clientIP, StringValue domain, ushort port)
        {
            var value = domain.Span;
            var userName = domain.Slice(1, value[0]);

            var userPwd = domain.Slice(1 + value[0] + 1, value[1 + value[0]]);
            var uLen = userName.Length + 1 + userPwd.Length + 1;

            var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(100);
            try
            {
                userName = userName.Split((byte)'@').First();// domain.Slice(1, value[0]);
                switch (value[uLen])
                {
                    case 0x03:
                        {
                            domain = domain.Slice(uLen + 2);
                            var ip = NetProxy.DNSToIPAsync(domain.UTF8()).Result.FirstOrDefault();
                            var (check, ipAddress, appPort, errorMsg) = Socks5Server.Check(userName, ip, port, clientIP, userPwd);
                            if (check != 0x00)
                            {
                                buffer[0] = 0x00;
                                buffer[1] = 0x00;
                                return buffer.AsSpan(0, 2).ToArray();
                            }
                            else
                            {

                                buffer[0] = 0x05;
                                buffer[1] = (byte)(appPort >> 8);
                                buffer[2] = (byte)(appPort & 0xff);
                                ipAddress.TryWriteBytes(buffer.AsSpan(3), out var qty);
                                // return
                                return buffer.AsSpan(0, qty + 3).ToArray();
                            }
                        }
                    case 0x01:
                        {
                            var ip2 = new IPAddress(value.Slice(uLen + 1, 4));
                            var (check, ipAddress, appPort, errorMsg) = Socks5Server.Check(userName, ip2, port, clientIP, userPwd);
                            if (check != 0x00)
                            {
                                buffer[0] = 0x00;
                                buffer[1] = 0x00;
                                return buffer.AsSpan(0, 2).ToArray();
                            }
                            else
                            {

                                buffer[0] = 0x05;
                                buffer[1] = (byte)(appPort >> 8);
                                buffer[2] = (byte)(appPort & 0xff);
                                ipAddress.TryWriteBytes(buffer.AsSpan(3), out var qty);
                                // return
                                return buffer.AsSpan(0, qty + 3).ToArray();
                            }
                        }
                    case 0x04:
                        {
                            var ip2 = new IPAddress(value.Slice(uLen + 1, 16));

                            var (check, ipAddress, appPort, errorMsg) = Socks5Server.Check(userName, ip2, port, clientIP, userPwd);
                            if (check != 0x00)
                            {
                                buffer[0] = 0x00;
                                buffer[1] = 0x00;
                                return buffer.AsSpan(0, 2).ToArray();
                            }
                            else
                            {

                                buffer[0] = 0x05;
                                buffer[1] = (byte)(appPort >> 8);
                                buffer[2] = (byte)(appPort & 0xff);
                                ipAddress.TryWriteBytes(buffer.AsSpan(3), out var qty);
                                // return
                                return buffer.AsSpan(0, qty + 3).ToArray();
                            }
                        }
                    default:
                        buffer[0] = 0x00;
                        buffer[1] = 0x00;
                        return buffer.AsSpan(0, 2).ToArray();
                }
            }
            finally
            {

                System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
            }
        }
        protected override Task WriteAsync(byte[] buffer, int offset, int count)
        {
            _OutputSize += count;
            this.targetStream.ActiveTime = Utility.TimeSpan();
            return targetStream.Socket.SendAsync(new ArraySegment<byte>(buffer, offset, count));
        }

        int ConfigKey;
        DataWriter dataWriter;
        public Socks5Server(DataWriter writer, StringValue value)
        {
            try
            {
                this.dataWriter = writer;
                var bytes = value.Span;
                var userName = value.Slice(1, bytes[0]);
                var size = bytes[0] + 1;
                var userPwd = value.Slice(size + 1, bytes[size]);
                size += bytes[size] + 1;
                switch (bytes[size])
                {
                    case 0x00:
                        bytes[size] = 0x04;
                        break;
                    case 0x01:
                        bytes[size] = 0x16;
                        break;
                }
                var domain = value.Slice(size + 1, bytes[size]);
                size += bytes[size] + 1;

                int port = (bytes[size] << 8) | bytes[size + 1];
                size += 2;

                var ip = value.Slice(size);
                this.Init(userName, new IPAddress(domain.Span), port, ip, userPwd);
            }
            catch
            {
                writer.Write(Array.Empty<byte>());

                _IsOver = true;
            }
        }


        byte[] buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(0x200);
        protected long _InputSize, _OutputSize;
        SocketAsyncEventArgs cleintSendArgs;
        public Socks5Server(Socket networkStream, int configKey, Span<byte> value)
        {
            this.ConfigKey = configKey;
            this.targetStream = new RelayAsyncArgs.Relay(networkStream);

            this.dataWriter = HttpBridgeServier.BridgeStart(configKey, value, this);

            this.cleintSendArgs = new SocketAsyncEventArgs();
            this.cleintSendArgs.SetBuffer(buffer);
            this.cleintSendArgs.Completed += ProxyCompleted;
            if (this.targetStream.IsConnect && !this.targetStream.Socket.ReceiveAsync(cleintSendArgs))
            {
                ProxyReceive(cleintSendArgs);
            }

        }
        private void ProxyReceive(SocketAsyncEventArgs e)
        {
            try
            {
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    this.targetStream.ActiveTime = Utility.TimeSpan();
                    _InputSize += e.BytesTransferred;
                    this.dataWriter.Write(buffer.AsSpan(0, e.BytesTransferred));
                    this.dataWriter.Flush();

                    while (this.targetStream.IsConnect && !this.targetStream.Socket.ReceiveAsync(e))
                    {
                        if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                        {
                            this.targetStream.ActiveTime = Utility.TimeSpan();
                            _InputSize += e.BytesTransferred;
                            this.dataWriter.Write(buffer.AsSpan(0, e.BytesTransferred));
                            this.dataWriter.Flush();
                        }
                        else
                        {
                            this.Dispose();
                            break;
                        }
                    }
                }
                else
                {
                    this.Dispose();
                }
            }
            catch// (Exception ex)
            {
                this.Dispose();
            }
        }
        private void ProxyCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                switch (e.LastOperation)
                {
                    case SocketAsyncOperation.Receive:
                        ProxyReceive(e);
                        break;

                }
            }
            else
            {
                this.Dispose();
            }
        }
        RelayAsyncArgs.Relay targetStream;
        private async void Init(StringValue userName, IPAddress domain, int port, StringValue clientIP, StringValue pwd)
        {
            try
            {
                var timeOut = new CancellationTokenSource(10000);
                var (check, ipAddress, appPort, errorMsg) = Check(userName, domain, port, clientIP, pwd);

                if (check != 0x00)
                {
                    buffer[0] = 0x05;
                    buffer[1] = check;
                    buffer[2] = (byte)errorMsg.Length;

                    this.dataWriter.Write(buffer.AsSpan(0, errorMsg.WriteBytes(buffer, 3) + 3));
                    this.dataWriter.Write(Array.Empty<byte>());
                }
                else
                {
                    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    await socket.ConnectAsync(ipAddress, appPort, timeOut.Token);


                    var ip = domain;
                    this.targetStream = new RelayAsyncArgs.Relay(socket);
                    buffer[0] = 0x05;
                    buffer[1] = 0x00;
                    buffer[2] = 0x00;
                    buffer[3] = 0x01;
                    ip.TryWriteBytes(buffer.AsSpan(4), out var _);

                    buffer[8] = (byte)(port >> 8);
                    buffer[9] = (byte)(port & 0xFF);
                    this.dataWriter.Write(buffer.AsSpan(0, 10));
                    // }
                    this.dataWriter.Flush();

                    this.cleintSendArgs = new SocketAsyncEventArgs();
                    this.cleintSendArgs.SetBuffer(buffer);
                    this.cleintSendArgs.Completed += ProxyCompleted;

                    if (this.targetStream.IsConnect && !this.targetStream.Socket.ReceiveAsync(cleintSendArgs))
                    {
                        ProxyReceive(cleintSendArgs);
                    }
                }
            }
            catch
            {
                this.dataWriter.Write(Array.Empty<byte>());

            }
            finally
            {
                this.dataWriter.Flush();
            }
        }
        public override void Dispose()
        {
            base.Dispose();
            this._IsOver = true;
            if (this.targetStream != null)
                this.targetStream.IsConnect = false;
            // this.targetStream?.Dispose();
            this.dataWriter?.Write(Array.Empty<byte>());
            this.dataWriter?.Flush();
            this.dataWriter?.Dispose();
            if (buffer.Length > 0)
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
                buffer = Array.Empty<byte>();
            }
            if (this.ConfigKey != 0)
            {
                HttpBridge.Statistical(this.ConfigKey, _InputSize, this._OutputSize);
            }

        }


        private static readonly (byte[] Prefix, int Mask)[] PrivateRanges =
           {
        // FE80::/10（链路本地地址）
        (new byte[] { 0xFE, 0x80 }, 10),
        // FC00::/7（唯一本地地址）
        (new byte[] { 0xFC }, 7),
        // ::1（环回地址）
        (new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, 128)
        };
        bool _IsOver;
        public override bool IsOver => _IsOver;

        public static bool IsInternalIP(ReadOnlySpan<byte> bytes)
        {
            if (bytes.Length != 16)
            {
                if (bytes[0] == 127) return true; // 10/8前缀
                if (bytes[0] == 10) return true; // 10/8前缀
                if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) return true; // 172.16/12前缀
                if (bytes[0] == 192 && bytes[1] == 168) return true; // 192.168/16前缀
                return false;
            }


            // 检查所有内网范围
            foreach (var range in PrivateRanges)
            {
                if (IsInRange(bytes, range.Prefix, range.Mask))
                {
                    return true;
                }
            }

            return false;
        }

        // 检查 IPv6 字节是否匹配前缀和掩码
        private static bool IsInRange(ReadOnlySpan<byte> addressBytes, byte[] prefix, int mask)
        {
            // 计算需要检查的字节数
            int byteCount = (mask + 7) / 8; // 向上取整
            if (prefix.Length < byteCount || addressBytes.Length < byteCount)
                return false;

            // 逐字节检查掩码位
            for (int i = 0; i < byteCount; i++)
            {
                int bitsToCheck = (i == byteCount - 1) ? (mask % 8) : 8;
                if (bitsToCheck == 0) bitsToCheck = 8;

                byte maskByte = (byte)(0xFF << (8 - bitsToCheck));
                byte addrByte = (byte)(addressBytes[i] & maskByte);
                byte prefixByte = (byte)(prefix[i] & maskByte);

                if (addrByte != prefixByte)
                {
                    return false;
                }
            }

            return true;
        }
        public class WebProxy : Mime
        {
            public WebProxy() : base(new ByteChunk())
            {
            }

            public StringValue UserAgent = StringValue.Empty;

            protected override bool Header(StringValue key, MimeHeadKey headKey, StringValue value)
            {
                if (headKey == MimeHeadKey.UserAgent)
                {
                    UserAgent = value;
                }
                return false;
            }
        }

        public static readonly StringValue ProxyDomain = new StringValue("proxy.apiumc.com");//proxy.apiumc.com


        private static async Task<(byte, IPAddress, int, String)> Check(StringValue userName, StringValue domain, int port, StringValue clientIP, StringValue pwd, CancellationToken cancellationToken = default)
        {

            var sIp = domain.UTF8();
            var ipAddresses = await Dns.GetHostAddressesAsync(sIp, cancellationToken);

            if (ipAddresses.Length == 0)
            {
                return (0x02, null, port, "此域名不能解释");
            }
            return Check(userName, ipAddresses[0], port, clientIP, pwd);

        }
        public static (byte, IPAddress, int, String) Check(StringValue userName, IPAddress ipAddress, int port, StringValue clientIP, StringValue pwd)
        {
            if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
            {
                var bufer = System.Buffers.ArrayPool<byte>.Shared.Rent(64);
                try
                {
                    bufer[0] = 0x00;
                    bufer[1] = 0x00;
                    ipAddress.TryWriteBytes(bufer.AsSpan(2), out var _);
                    ushort bport = (ushort)port;
                    BitConverter.TryWriteBytes(bufer.AsSpan(6), bport);
                    var deviceId = BitConverter.ToUInt64(bufer);
                    var device = HotCache.Get(new Device { Id = deviceId });

                    if (device != null)
                    {
                        if (userName.IsEmpty)
                        {
                            return (0x03, ipAddress, port, "访问设备资源用户名不能为空");
                        }

                        Utility.MD5(userName.Span, bufer.AsSpan(8));

                        BitConverter.TryWriteBytes(bufer.AsSpan(clientIP.Length + 8), Utility.IntParse(bufer.AsSpan(8, 16)));

                        clientIP.Span.CopyTo(bufer.AsSpan(8));

                        var dUser = SocksUser.Cache.Get(new SocksUser
                        {
                            ClientIP = new StringValue(bufer, 8, clientIP.Length + 4),
                            Port = new StringValue(bufer, 6, 2),
                            UserAgent = new StringValue(bufer, 2, 4)
                        });

                        var exTime = dUser?.ExpirationTime.UInt32().Value ?? 0;
                        var now = Utility.TimeSpan();
                        if (exTime < now)
                        {
                            return (0x03, ipAddress, port, "未授权或授权已过期");
                        }
                        else if (pwd.Length == 20)
                        {
                            var pwdSpan = pwd.Span;

                            var time = System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(pwdSpan) + 0L;

                            if (Math.Abs(UMC.Data.Utility.TimeSpan() + 0L - time) > 300)
                            {
                                return (0x03, ipAddress, port, "未授权或授权已过期");
                            }
                            pwdSpan.Slice(0, 4).CopyTo(bufer);
                            dUser.Username.Span.CopyTo(bufer.AsSpan(4));
                            var size = dUser.Username.Length + 4;
                            dUser.SessionKey.Span.CopyTo(bufer.AsSpan(size));
                            size += 16;

                            var md5 = bufer.AsSpan(size, 16);
                            Utility.MD5(bufer.AsSpan(0, size), md5);
                            if (md5.SequenceEqual(pwd.Span.Slice(4)) == false)
                            {
                                return (0x03, ipAddress, port, "未授权或授权已过期");
                            }

                            var value = exTime - dUser.ActiveTime.UInt32().Value;
                            BitConverter.TryWriteBytes(dUser.ExpirationTime.Span, now + value);
                            BitConverter.TryWriteBytes(dUser.ActiveTime.Span, now);

                        }
                        else if (userName.Span.SequenceEqual(dUser.Username.Span) == false || pwd.Span.SequenceEqual(dUser.SessionKey.Span) == false)
                        {
                            return (0x03, ipAddress, port, "未授权或授权已过期");
                        }
                        return (0x00, ipAddress, device.Port ?? port, String.Empty);
                    }
                    else if (IsInternalIP(ipAddress.GetAddressBytes()))
                    {
                        return (0x02, ipAddress, port, "此内网资源未注册");

                    }
                }
                finally
                {
                    System.Buffers.ArrayPool<byte>.Shared.Return(bufer);
                }
            }

            return (0x00, ipAddress, port, String.Empty);

        }
        public static void OR(Span<byte> stringValue, byte value)
        {

            var f = stringValue;
            for (var i = 0; i < f.Length; i++)
            {
                f[i] ^= value;
            }
        }

        public static async void HandleClientAsync(Socket client, Memory<byte> header)
        {

            var timeOut = new CancellationTokenSource(60000);
            var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(512);
            Socket proxySocket = null;
            var isSocketProxy = false;
            try
            {
                proxySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                var pLocalPort = (ushort)(client.LocalEndPoint as IPEndPoint).Port;
                var address = (client.LocalEndPoint as IPEndPoint).Address;
                if (address.IsIPv4MappedToIPv6)// == AddressFamily.InterNetworkV6)
                {
                    address = address.MapToIPv4();
                }
                address.TryWriteBytes(buffer, out var bLeng);

                var clientIP = new StringValue(buffer.AsSpan(0, bLeng).ToArray());

                header.CopyTo(buffer);
                StringValue userName = StringValue.Empty;
                StringValue userPwd = StringValue.Empty;

                int l = 0;
                int plUserIndex = -1;
                if (buffer.AsSpan(2, buffer[1]).Contains((byte)0x02))
                {
                    buffer[0] = 0x05;
                    buffer[1] = 0x02;
                    await client.SendAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);

                    l = await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);
                    var userLen = buffer[1];
                    l = await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, userLen + 1), timeOut.Token);
                    if (l != userLen + 1)
                    {
                        buffer[0] = 0x05;
                        buffer[1] = 0x02;
                        await client.SendAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);
                        return;
                    }
                    userName = new StringValue(buffer.AsSpan(0, l - 1).ToArray());

                    plUserIndex = userName.Span.IndexOf("@"u8);
                    var pwdLen = buffer[userLen];
                    if (pwdLen > 0)
                    {
                        l = await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, pwdLen), timeOut.Token);
                        if (l != pwdLen)
                        {

                            buffer[0] = 0x05;
                            buffer[1] = 0x02;
                            await client.SendAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);
                            return;
                        }
                        userPwd = new StringValue(buffer.AsSpan(0, l).ToArray());

                    }
                    if (userName.IsEmpty)
                    {
                        buffer[0] = 0x05;
                        buffer[1] = 0x02;
                        await client.SendAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);
                        return;
                    }


                    if (plUserIndex == -1 && userPwd.IsEmpty == false)
                    {

                        userName.Span.CopyTo(buffer);
                        clientIP.Span.CopyTo(buffer.AsSpan(userName.Length));
                        WebServlet.AppSecret.Span.CopyTo(buffer.AsSpan(userName.Length + clientIP.Length));
                        var size = userName.Length + clientIP.Length + 16;

                        Utility.MD5(buffer.AsSpan(0, size), buffer.AsSpan(size));
                        if (userPwd.Span.SequenceEqual(buffer.AsSpan(size, 16)) == false)
                        {
                            buffer[0] = 0x05;
                            buffer[1] = 0x03;
                            buffer[2] = (byte)clientIP.Length;
                            clientIP.Span.CopyTo(buffer.AsSpan(3));
                            await client.SendAsync(new ArraySegment<byte>(buffer, 0, clientIP.Length + 3), timeOut.Token);
                            return;

                        }

                    }

                    buffer[0] = 0x05;
                    buffer[1] = 0x00;
                    await client.SendAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);
                }
                else
                {

                    buffer[0] = 0x05;
                    buffer[1] = 0x02;
                    await client.SendAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);
                    return;
                }

                l = await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, 4), timeOut.Token);

                if (buffer[1] != 0x01)
                {
                    return;
                }

                StringValue domain = StringValue.Empty;
                IPAddress destAddress = IPAddress.None;
                switch (buffer[3])
                {
                    case 0x02:
                        await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, 1), timeOut.Token);

                        var length2 = buffer[0];
                        await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, length2), timeOut.Token);
                        domain = new StringValue(buffer.AsSpan(0, length2).ToArray());
                        OR(domain.Span, 0x02);
                        break;
                    case 0x03:
                        await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, 1), timeOut.Token);
                        var length = buffer[0];
                        await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, length), timeOut.Token);
                        domain = new StringValue(buffer.AsSpan(0, length).ToArray());
                        break;
                    case 0x01:
                        await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, 4), timeOut.Token);
                        destAddress = new IPAddress(buffer.AsSpan(0, 4));//, 0, 4);
                        break;
                    case 0x04:
                        await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, 16), timeOut.Token);
                        destAddress = new IPAddress(buffer.AsSpan(0, 16));//, 0, 4);
                        break;
                    default:
                        buffer[0] = 0x05;
                        buffer[1] = 0x05;
                        await client.SendAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);
                        return;
                }


                await client.ReceiveAsync(new ArraySegment<byte>(buffer, 0, 2), timeOut.Token);

                var portBuffer = buffer.AsSpan(0, 2).ToArray();
                if (plUserIndex > 0)
                {

                    var configKey = Utility.Parse36Decode(userName.Slice(plUserIndex + 1).UTF8());
                    if (Utility.IntParse(WebServlet.AppId.Span) != configKey)
                    {
                        if (HttpBridgeServier.CheckBridgeServer(configKey) == false)
                        {
                            buffer[0] = 0x05;
                            buffer[1] = 0x02;
                            buffer[2] = (byte)"转发服务未开启".WriteBytes(buffer, 3);
                            await client.SendAsync(new ArraySegment<byte>(buffer, 0, buffer[2] + 3), timeOut.Token);
                        }
                        else
                        {
                            //
                            var traffic = HttpBridge.BridgeSiteCache.Get(new BridgeSite { Key = configKey });

                            if ((traffic?.AllowSize ?? 0) < 0)
                            {
                                buffer[0] = 0x05;
                                buffer[1] = 0x04;
                                buffer[2] = (byte)"转发流量已用尽，请充值".WriteBytes(buffer, 3);
                                await client.SendAsync(new ArraySegment<byte>(buffer, 0, buffer[2] + 3), timeOut.Token);
                                return;
                            }
                            buffer[0] = 0x05;
                            buffer[1] = (byte)plUserIndex;
                            userName.Slice(0, plUserIndex).Span.CopyTo(buffer.AsSpan(2));
                            var size = plUserIndex + 2;

                            buffer[size] = (byte)userPwd.Length;
                            size += 1;

                            userPwd.Span.CopyTo(buffer.AsSpan(size));
                            size += userPwd.Length;
                            if (domain.IsEmpty())
                            {
                                size += 1;
                                destAddress.TryWriteBytes(buffer.AsSpan(size), out var bytesWritten);
                                if (bytesWritten == 16)
                                {
                                    buffer[size - 1] = 0x1;
                                }
                                else
                                {

                                    buffer[size - 1] = 0x00;
                                }
                                size += bytesWritten;
                            }
                            else
                            {
                                buffer[size] = (byte)domain.Length;
                                size += 1;
                                domain.Span.CopyTo(buffer.AsSpan(size));
                                size += domain.Length;

                            }

                            portBuffer.AsSpan().CopyTo(buffer.AsSpan(size));
                            size += 2;
                            clientIP.Span.CopyTo(buffer.AsSpan(size));
                            size += clientIP.Length;
                            isSocketProxy = true;
                            new Socks5Server(client, configKey, buffer.AsSpan(0, size));
                        }
                        return;
                    }
                    else
                    {
                        userName = userName.Slice(0, plUserIndex);
                    }
                }
                int port = (portBuffer[0] << 8) | portBuffer[1];
                var (check, ipAddress, appPort, errorMsg) = domain.IsEmpty() ? Check(userName, destAddress, port, clientIP, userPwd) : await Check(userName, domain, port, clientIP, userPwd, timeOut.Token);
                if (check != 0x00)
                {
                    buffer[0] = 0x05;
                    buffer[1] = check;
                    buffer[2] = (byte)errorMsg.Length;
                    await client.SendAsync(new ArraySegment<byte>(buffer, 0, errorMsg.WriteBytes(buffer, 3) + 3), timeOut.Token);
                    return;
                }

                if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    buffer[0] = 0x05;
                    buffer[1] = 0x00;
                    buffer[2] = 0x00;
                    buffer[3] = 0x04;
                    ipAddress.TryWriteBytes(buffer.AsSpan(4), out var _);
                    BitConverter.TryWriteBytes(buffer.AsSpan(20), (ushort)port);
                    await client.SendAsync(new ArraySegment<byte>(buffer, 0, 22), timeOut.Token);
                }
                else
                {
                    buffer[0] = 0x05;
                    buffer[1] = 0x00;
                    buffer[2] = 0x00;
                    buffer[3] = 0x01;
                    ipAddress.TryWriteBytes(buffer.AsSpan(4), out var _);
                    portBuffer.CopyTo(buffer.AsSpan(8));
                    await client.SendAsync(new ArraySegment<byte>(buffer, 0, 10), timeOut.Token);
                }
                await proxySocket.ConnectAsync(ipAddress, appPort, timeOut.Token);

                isSocketProxy = true;
                var clientRelay = new RelayAsyncArgs.Relay(client);
                var proxyRelay = new RelayAsyncArgs.Relay(proxySocket);
                new RelayAsyncArgs(clientRelay, proxyRelay);
                new RelayAsyncArgs(proxyRelay, clientRelay);

            }
            catch //(Exception ex)
            {
                isSocketProxy = false;
            }
            finally
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
                if (isSocketProxy == false)
                {
                    proxySocket?.Close();
                    proxySocket?.Dispose();
                    client.Dispose();
                }
            }

        }
    }
}
